SERIALLY_REUSABLE Packages

Por Tércio Costa
Postado em Maio 2016
Revisado por Marcelo Pivovar - Solution Architect

Nostrud Nulla Laborum Occaecat

Packages é um componente muito importante do Oracle Database. Com ele podemos agrupar funções, procedures, variáveis, tipos de dados e etc, como componentes em uma biblioteca armazenada no banco, onde vários usuários poderão ter acesso e se beneficiar de tudo que tem no package.

Vamos entender um pouco sobre o funcionamento do package, como ele é inicializado, onde é armazenado, como os seus componentes são compartilhados entre os usuários e assim entender possíveis problemas que podemos enfrentar, e para finalizar, como a PRAGMA SERIALLY_REUSABLE pode nos ajudar nessa situação.

Quando a sessão de um usuário faz referência a um componente do package, o Oracle cria uma instância para aquela sessão. Cada sessão que fizer referência ao mesmo package irá ter a sua própria instância. Essa inicialização do package inclui atribuir valores iniciais a constantes e variáveis públicas e executando a inicialização do package body.

Somente com essas informações podemos prever algo. Como o Oracle cria uma instância do package para cada sessão que faz referência ao mesmo, sendo assim ele é armazenado na User Global Area(UGA). A quantidade de memória necessária na UGA irá aumentar linearmente com o número de usuários, consumindo muita memória e limitando a escalabilidade. Para piorar a situação, essa memória utilizada pelo package persiste até que a sessão seja finalizada, o que as vezes pode demorar bastante, mesmo sem o usuário utilizar mais nada do package!

Algo também pode acontecer mas que pode não ser o desejado, é ao se utilizar variáveis na especificação do package. Se alterarmos esse valor em algum momento da nossa aplicação, uma segunda chamada a essa variável não terá o valor original dela, e sim o modificado, e conforme já foi dito, isso talvez não seja o desejado. Isso é exemplificado na Listagem 1.

Listagem 1. Variáveis em um package não SERIALLY_REUSABLE:



01 CREATE OR  REPLACE PACKAGE exemplo_otn IS 
02   pv_otn NUMBER := 1; 
03 END  exemplo_otn; 
04 / 
05 
06 BEGIN 
07   dbms_output.put_line('Variável OTN:  '||exemplo_otn.pv_otn); 
08   exemplo_otn.pv_otn := 2; 
09   dbms_output.put_line('Variável OTN:  '||exemplo_otn.pv_otn); 
10 END; 
11 / 

Nas linhas 01 à 03 foi criado um package bastante simples, apenas para demonstração, com apenas uma variável com valor inicial 1. Nas linhas 06 a 10 foi criado um bloco PL/SQL anônimo para testar o comportamento da variável pública. Na linha 07 o valor é impresso, na linha 08 o valor é alterado e para finalizar o valor é impresso novamente na linha 09. Vejam o resultado disso na Listagem 2.

Listagem 2. Retorno obtido da Listagem 1.



01 Variável OTN: 1 
02 Variável OTN: 2 

Reparem que a primeira chamada a variável, retornou o valor 1 que era o seu valor inicial. Após alterar o valor para 2, da variável, a segunda chamada, e todas as chamadas subsequentes, terão o valor 2, até que esse valor seja alterado novamente. Para exemplificar, se o bloco anônimo, da Listagem 1, for executado novamente, ele irá imprimir o valor 2 duas vezes e não mais uma, pois o valor 2 ainda estará salvo na UGA!

Com um package sendo declarado como SERIALLY_REUSABLE o estado do package é armazenado no Work Area em uma pool na System Global Area(SGA) e não mais na UGA. Ele persiste somente durante a chamada ao servidor de algum componente do package. Depois dessa chamada, a Work Area é retornada ao pool. Se algum usuário, talvez o mesmo, fizer mais uma chamada, o Oracle irá reutilizar a instância da pool, mas reinicializando-a. Com isso, todas as alterações anteriores feitas no package serão perdidas, pois o Oracle irá atribuir novamente os valores iniciais as variáveis. Outra grande vantagem nisso é a economia de memória, melhorando assim a escalabilidade da nossa aplicação.

Criando um Package SERIALLY_REUSABLE

Vejamos agora, como ficaria o mesmo exemplo anterior, mas agora sendo SERIALLY_REUSABLE.

Listagem 3. SERIALLY_REUSABLE Package:



01 CREATE OR  REPLACE PACKAGE exemplo_otn_sr IS 
02   PRAGMA SERIALLY_REUSABLE; 
03   pv_otn NUMBER := 1; 
04 END exemplo_otn_sr; 
05 / 
 

Na linha 02 da Listagem 3, adicionamos o PRAGMA SERIALLY_REUSABLE na especificação do nosso package. Como o nosso exemplo foi bastante simples, contendo apenas uma variável, não foi preciso criar um body para o package. Mas caso fosse necessário, o PRAGMA SERIALLY_REUSABLE também teria que estar no package body. Vejamos agora o efeito de um package ser SERIALLY_REUSABLE. Vamos utilizar o package que foi criado na Listagem 1 e na Listagem 3.

Listagem 4. Efeito do SERIALLY_REUSABLE:



01 BEGIN 
02   --alterar os valores das  variáveis públicas do dois package 
03   exemplo_otn.pv_otn := 2; 
04   exemplo_otn_sr.pv_otn := 2; 
05 END; 
06 / 
07 
08 BEGIN 
09   dbms_output.put_line(‘Sem ser SERIALLY_REUSABLE: ’||exemplo_otn.pv_otn); 
10   dbms_output.put_line(‘Sendo SERIALLY_REUSABLE: ’||exemplo_otn_sr.pv_otn); 
11 END; 
12 / 
 
 

No primeiro bloco, nas linhas 01 à 05, foi feito uma alteração nos valores das variáveis de cada package, sendo um SERIALLY_REUSABLE e o outro não. No segundo bloco anônimo, nas linhas 08 à 11, é exibido o valor de cada uma das variáveis dos seus respectivos packages, vejamos agora a diferença entre os dois na listagem a seguir. Lembrando que por se tratar de blocos PL/SQL diferentes, são consideradas duas chamadas ao servidor diferentes.

Listagem 5. Retorno obtido da Listagem 4:



01 Sem ser SERIALLY_REUSABLE: 2 
02 Sendo SERIALLY_REUSABLE: 1 
 

Perceba a diferença dos valores retornados. Primeiro o package sem ser SERIALLY_REUSABLE retorna o valor 2, que foi o valor modificado no bloco anterior, isso acontece por que o package ainda reside na memória, na UGA. Já no caso do pacakge SERIALLY_REUSABLE o valor retornou para o inicial,que no caso foi 1. Isso ocorro por que, após uma chamada no servidor, que foi o caso do primeiro bloco, o Oracle fecha qualquer cursor aberto, libera memória e retorna o package para o pool, para que ele possa ser reutilizado. Mas na próxima chamada ele terá os valores iniciais novamente, que foi exatamente o que aconteceu.

>Tércio Costa Formado em Ciências da Computação pela UFPB com experiência em Servidores Windows Server e Linux e banco de dados Oracle desde 2008 juntamente com os seus serviços. Desenvolvimento de Sistemas em Java SE com banco de dados Oracle e MySQL. Certificado Oracle Certified SQL Expert, mantendo o blog https://oraclepress.wordpress.com/ reconhecido pela OTN e articulista no portal http://www.profissionaloracle.com.br/gpo

Este artigo foi revisto pela equipe de produtos Oracle e está em conformidade com as normas e práticas para o uso de produtos Oracle.